﻿/*****************************************************************************
	SNEGA STG GAME ENGINE 0.7a
	Copyright (C) 2009, Xiaohan Tian (snega0223)
	
	http://code.google.com/p/sng-actionscript-stg-engine/
	
	This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License(LGPL) as
	published by the Free Software Foundation, either version 3 of the 
	License, or(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Lesser General Public License for more details.
	
	You should have received a copy of the GNU Lesser General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/

package com.sng.stg.game 
{
	import away3d.cameras.*;
	import away3d.containers.*;
	import away3d.core.base.Object3D;
	import away3d.core.utils.*;
	import away3d.lights.*;
	import away3d.materials.*;
	import away3d.primitives.*;
	import away3d.loaders.Collada;
	import away3d.loaders.Object3DLoader;
	import away3d.core.math.Number3D;
	import com.sng.stg.game.bullet.EnemyBullet;
	import com.sng.stg.game.bullet.PlayerBullet;
	import com.sng.stg.game.enemy.Enemy;
	import com.sng.stg.game.event.ClearStage;
	import com.sng.stg.game.event.ContinuousEvent;
	import com.sng.stg.game.event.CreateEnemy;
	import com.sng.stg.game.event.GameEvent;
	import com.sng.stg.game.event.PassStage;
	import com.sng.stg.game.event.ShowStageTitle;
	import com.sng.stg.game.Player.Player;
	import flash.text.TextField;
	import flash.text.TextFormat;
	import org.papervision3d.scenes.Scene3D;
	
	import flash.geom.*;
	import flash.display.*;
	import flash.events.*;
	import flash.xml.*;
	import flash.net.*;
	
	/**
	 * ...
	 * @author snega
	 */
	public class Stage extends StageViewer
	{		
		protected var _stageConf:XML = null;
		protected var _stageConfLoader:URLLoader = null;
		
		protected var _playerLoader:Loader = null;
		protected var _playerBulletLoader:Loader = null;
		
		protected var _lastPlayer:Player = null;
		
		protected var _load3DComplete:Boolean = false;
		protected var _load2DComplete:Boolean = false;
		
		protected var _modelNum:int = 0;
		protected var _modelLoadedNum:int = 0;
		protected var _modelLoader:Object3DLoader = null;
		protected var _modelXmlList:XMLList = null;
		
		protected var _imageNum:int = 0;
		protected var _imageLoadedNum:int = 0;
		protected var _imageLoader:Loader = null;
		protected var _imageXmlList:XMLList = null;
		
		protected var _loading:TextField = new TextField();
		protected var _loadingFormat:TextFormat = new TextFormat();
		
		public function Stage() 
		{
			super(390, 512, 10, 100, null, 3300, 0, 45);
			
			this._loadingFormat.font = "Palatino Linotype";
			this._loadingFormat.color = 0xFFFFFF;
			
			this._loading.x = 10;
			this._loading.y = 10;
			this._loading.width = 300;
			this._loading.setTextFormat(this._loadingFormat);
		}
		
		public function go(stageUrl:String):void {
			trace("loading stage: " + stageUrl);
			
			this._2dMask.addChild(new Bitmap(this._imageBitmapMap["loading"]));
			
			// load stage configuration xml
			this._stageConfLoader = new URLLoader(new URLRequest(stageUrl));
			this._stageConfLoader.addEventListener(Event.COMPLETE, onStageConfLoadComplete);
			
			this.addEventListener(Event.ENTER_FRAME, onEnterLoadingFrame);
			
			// "loading" text			
			this._2dMask.addChild(this._loading);
		}
		
		public function onStageConfLoadComplete(e:Event):void
		{
			trace("xml configuration loaded");
			
			this._stageConf = new XML(this._stageConfLoader.data);
			
			// load 3d object
			this._modelXmlList = this._stageConf.load.models;
			
			trace("find " + this._stageConf.load.model_number.toString() + " models.");
			
			this._modelNum = parseInt(this._stageConf.load.model_number.toString());
			this._modelLoadedNum = 0;
			
			if (this._modelNum > 0) {
				var str:String = this._modelXmlList.model[this._modelLoadedNum].toString()
				
				trace("loading model: " + str);
				this._modelLoader = Collada.load(str);
				this._modelLoader.addOnSuccess(onModelLoaderComplete);
			}
			else {
				this._load3DComplete = true;
			}
			
			// load 2d images
			this._imageXmlList = this._stageConf.load.images;
			
			trace("find " + this._stageConf.load.image_number.toString() + " images.");
			
			this._imageNum = parseInt(this._stageConf.load.image_number.toString());
			this._imageLoadedNum = 0;
			
			if (this._imageNum > 0) {
				this._imageLoader = new Loader();
				
				str = this._imageXmlList.image[this._imageLoadedNum].toString()
				trace("loading image: " + str);
				this._imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onImageLoaderComplete);
				this._imageLoader.load(new URLRequest(str));
			}
			else {
				this._load2DComplete = true;
			}
		}
		
		public function process3DElement():void
		{
			this._offsetPerFrame.z = 10;
			
			this._elemtntZLength = parseInt(this._stageConf.configuration.stagebg.@zlength);
			
			this.setElement(Object3D(this._modelMap[this._stageConf.configuration.stagebg.@model]).clone(), 1, 3, new Number3D( -35, 0, 0 ));
		}
		
		public function onModelLoaderComplete(e:Event):void {
			trace("push model to map: " + this._modelXmlList.model[this._modelLoadedNum].@id);
			this._modelMap[this._modelXmlList.model[this._modelLoadedNum].@id] = this._modelLoader.handle.clone();
			
			this._modelLoadedNum++;
			
			if (this._modelLoadedNum == this._modelNum) {
				this._load3DComplete = true;
			}
			else {
				var str:String = this._modelXmlList.model[this._modelLoadedNum].toString()
				
				trace("loading model: " + str);
				this._modelLoader = Collada.load(str);
				this._modelLoader.addOnSuccess(onModelLoaderComplete);
			}
		}
		
		public function processPlayer():void
		{
			this._player = new Player(this);
			var playerBitmap:Bitmap = new Bitmap(this._imageBitmapMap["player"]);
			playerBitmap.x -= playerBitmap.width / 2;
			playerBitmap.y -= playerBitmap.height / 2;
			this._player.addChild(playerBitmap);
			
			if (this._lastPlayer != null) {
				this._player._graze = this._lastPlayer._graze;
				this._player._life = this._lastPlayer._life;
				this._player._point = this._lastPlayer._point;
				
				this._lastPlayer = null;
			}
		}
		
		public function processPlayerBullet():void
		{
			this._playerBulletData = this._imageBitmapMap["player_bullet"];
			this._playerBullet = new Bitmap(this._playerBulletData);
		}
		
		public function onImageLoaderComplete(e:Event):void {
			var loadedImage:Bitmap = Bitmap(this._imageLoader.content);
			
			var bitmap:BitmapData = new BitmapData(loadedImage.width, loadedImage.height, true, 0x00ffffff);
			bitmap.draw(loadedImage, new Matrix());
			
			trace("push image to map: " + this._imageXmlList.image[this._imageLoadedNum].@id);
			this._imageBitmapMap[this._imageXmlList.image[this._imageLoadedNum].@id] = bitmap;
			
			this._imageLoadedNum++;
			
			if (this._imageLoadedNum == this._imageNum) {
				this._load2DComplete = true;
			}
			else {
				this._imageLoader = new Loader();
				
				var str:String = this._imageXmlList.image[this._imageLoadedNum].toString()
				trace("loading image: " + str);
				this._imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onImageLoaderComplete);
				this._imageLoader.load(new URLRequest(str));
			}
		}
		
		protected function analyzeEvent():void {
			// analyze event list
			var action:GameEvent = null;
			var totalTime:int = 0;
			for each (var currEvent:XML in this._stageConf.events.elements()) {
				trace("find event: " + currEvent.@type + " (time = " + currEvent.@time + ")");
				
				totalTime += parseInt(currEvent.@time);
				
				if (currEvent.@type == "create_enemy") {
					action = new CreateEnemy(totalTime);
					action.load(currEvent, this);
					this._eventList.pushToEnd(action);
				}
				else if (currEvent.@type == "clear_stage") {
					action = new ClearStage(totalTime);
					this._eventList.pushToEnd(action);
				}
				else if (currEvent.@type == "show_stage_title") {
					action = new ShowStageTitle(totalTime, this);
					this._eventList.pushToEnd(action);
				}
				else if (currEvent.@type == "pass_stage") {
					action = new PassStage(totalTime, currEvent.@src);
					this._eventList.pushToEnd(action);
				}
				else {
					action = new GameEvent(totalTime);
					this._eventList.pushToEnd(action);
				}
			}
		}
		
		protected function onEnterLoadingFrame( ThisEvent:Event ):void {
			this._loading.text = "loading stage ... (" + (this._imageLoadedNum + this._modelLoadedNum) + "/" + (this._imageNum + this._modelNum) + ")";
			this._loading.setTextFormat(this._loadingFormat);

			if (this._load3DComplete && this._load2DComplete) {
				// initialize
				this.process3DElement();
				this.processPlayer();
				this.processPlayerBullet();
				this.analyzeEvent();
				this.removeEventListener(Event.ENTER_FRAME, onEnterLoadingFrame);
				
				// clear 2d mask
				var i:int = 0, max:int = this._2dMask.numChildren;
				for (i = 0; i < max; i++) this._2dMask.removeChildAt(0);
				trace("removed " + max + " object in 2d mask");
				
				this.start();
			}
		}
		
		override public function reset():void {
			trace("reset stage");
			
			this.removeEventListener(Event.ENTER_FRAME, onEnterFrame);
			
			// reset all list
			this._elemtntZLength = 0;
			
			this._offsetPerFrame = new Number3D(0, 0, 0);
			this._offsetLimiter = new Number3D(0, 0, 0);
			
			this._3dFrameSkip = 2;
			this._3dFrame = 0;
			this._playerBullentList = new PlayerBullet(this);
			this._playerEnable = true;
			
			this._currEnemyList = new Enemy();
			this._enemyBulletList = new EnemyBullet(this);
			this._eventList = new GameEvent(0);
			this._currEvent = null;
			this._continuousEventList = new ContinuousEvent();
			
			// remove all object from all list
			var max:int = 0, i:int = 0;
			
			max = this._background.numChildren;
			for (i = 0; i < max; i++) this._background.removeChildAt(0);
			
			this._view.scene.removeChild(this._elements);
			this._elements = null;
			
			max = this._viewMask.numChildren;
			for (i = 0; i < max; i++) this._viewMask.removeChildAt(0);
			
			max = this._2dElement.numChildren;
			for (i = 0; i < max; i++) this._2dElement.removeChildAt(0);
			
			max = this._scoreboard.numChildren;
			for (i = 0; i < max; i++) this._scoreboard.removeChildAt(0);
			
			max = this._2dMask.numChildren;
			for (i = 0; i < max; i++) this._2dMask.removeChildAt(0);
			
			// reset all parameters
			this._camera.zoom = 10;
			this._camera.focus = 100;
			this._camera.x = 0;
			this._camera.y = 0;
			this._camera.z = 0;
			this._cameraTarget.x = 0;
			this._cameraTarget.y = 0;
			this._cameraTarget.z = 0;
			this._camera.lookAt(this._cameraTarget);
			
			this._cameraRotY = 45;
			this._cameraRotX = 0;
			this._cameraDis = 3300;
			
			this._time = 0;
			this._frame = 0;
			
			this.init3d();
			this.init2d();
			
			// restart
			this.onEnterLoadingFrame(null);
		}
		
		override public function reload(url:String = null):void {
			trace("reload stage");
			
			this.removeEventListener(Event.ENTER_FRAME, onEnterFrame);
			
			// reset all list
			this._elemtntZLength = 0;
			
			this._offsetPerFrame = new Number3D(0, 0, 0);
			this._offsetLimiter = new Number3D(0, 0, 0);
			
			this._3dFrameSkip = 2;
			this._3dFrame = 0;
			this._playerBullentList = new PlayerBullet(this);
			this._playerEnable = true;
			
			this._currEnemyList = new Enemy();
			this._enemyBulletList = new EnemyBullet(this);
			this._eventList = new GameEvent(0);
			this._currEvent = null;
			this._continuousEventList = new ContinuousEvent();
			
			// remove all object from all list
			var max:int = 0, i:int = 0;
			
			max = this._background.numChildren;
			for (i = 0; i < max; i++) this._background.removeChildAt(0);
			
			this._view.scene.removeChild(this._elements);
			this._elements = null;
			
			max = this._viewMask.numChildren;
			for (i = 0; i < max; i++) this._viewMask.removeChildAt(0);
			
			max = this._2dElement.numChildren;
			for (i = 0; i < max; i++) this._2dElement.removeChildAt(0);
			
			max = this._scoreboard.numChildren;
			for (i = 0; i < max; i++) this._scoreboard.removeChildAt(0);
			
			max = this._2dMask.numChildren;
			for (i = 0; i < max; i++) this._2dMask.removeChildAt(0);
			
			// reset all parameters
			this._camera.zoom = 10;
			this._camera.focus = 100;
			this._camera.x = 0;
			this._camera.y = 0;
			this._camera.z = 0;
			this._cameraTarget.x = 0;
			this._cameraTarget.y = 0;
			this._cameraTarget.z = 0;
			this._camera.lookAt(this._cameraTarget);
			
			this._cameraRotY = 45;
			this._cameraRotX = 0;
			this._cameraDis = 3300;
			
			this._time = 0;
			this._frame = 0;
			
			this.init3d();
			this.init2d();
			
			var player:Player = this._player;
			
			// reload
			if (url == null) {
				this.onEnterLoadingFrame(null);
			}
			else {
				this._load2DComplete = false;
				this._load3DComplete = false;
				
				this._lastPlayer = this._player;
				
				this.go(url);
			}
		}
		
	}
	
}